package com.qinglei.recoup.system.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.qinglei.recoup.app.pojo.UserInfo;
import com.qinglei.recoup.app.pojo.WxLoginInfo;
import com.qinglei.recoup.common.authentication.JWTToken;
import com.qinglei.recoup.common.authentication.JWTUtil;
import com.qinglei.recoup.common.domain.ActiveUser;
import com.qinglei.recoup.common.domain.CommonConstant;
import com.qinglei.recoup.common.domain.CommonRes;
import com.qinglei.recoup.common.exception.RedisConnectException;
import com.qinglei.recoup.common.properties.CuserProperties;
import com.qinglei.recoup.common.properties.RecoupProperties;
import com.qinglei.recoup.common.service.CacheService;
import com.qinglei.recoup.common.service.RedisService;
import com.qinglei.recoup.common.utils.AesEncryptUtil;
import com.qinglei.recoup.common.utils.DateUtil;
import com.qinglei.recoup.common.utils.JsonUtils;
import com.qinglei.recoup.common.utils.MD5Util;
import com.qinglei.recoup.hmp.domain.HmpWechatMpFan;
import com.qinglei.recoup.hmp.service.HmpWechatMpFanService;
import com.qinglei.recoup.hmp.service.HmpWechatMpService;
import com.qinglei.recoup.system.domain.Tenant;
import com.qinglei.recoup.system.domain.TenantThemeBO;
import com.qinglei.recoup.system.domain.User;
import com.qinglei.recoup.system.enums.EventEnum;
import com.qinglei.recoup.system.manager.UserManager;
import com.qinglei.recoup.system.service.EventLogService;
import com.qinglei.recoup.system.service.LoginService;
import com.qinglei.recoup.system.service.NurseOnlineService;
import com.qinglei.recoup.system.service.TenantService;
import com.qinglei.recoup.system.service.UserService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import springfox.bean.validators.plugins.schema.MinMaxAnnotationPlugin;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Service
@Slf4j
public class LoginServiceImpl implements LoginService {
    @Autowired
    private RecoupProperties properties;

    @Autowired
    private RedisService redisService;

    @Autowired
    private UserManager userManager;

    @Autowired
    private UserService userService;

    @Autowired
    private NurseOnlineService nurseOnlineService;

    @Resource
    private EventLogService eventLogService;

    @Resource
    private CacheService cacheService;

    @Resource
    private TenantService tenantService;

    @Value("${recoup.tenant.theme.default-banner-url}")
    private String defaultBannerUrl;
    @Value("${recoup.tenant.theme.default-banner-color}")
    private String defaultBannerColor;

    @Autowired
    private HmpWechatMpFanService hmpWechatMpFanService;

    @Override
    public CommonRes login(String username, String password) throws Exception {
        log.info("login username:{}, password:{}", username, password);
        username = StringUtils.lowerCase(username);
        User user = this.userManager.getUser(username);

        final String errorMessage = "账号和密码不匹配";
        // 检查账号是否存在
        if (user == null) {
            return CommonRes.failed(errorMessage);
        }
        try {
            password = MD5Util.encrypt(AesEncryptUtil.decrypt(password));
        } catch (Exception e) {
            log.error("解密密码过程出错", e);
        }
        // 检查密码是否匹配
        if (StringUtils.isEmpty(password) || !StringUtils.equals(user.getPassword(), password)) {
            return CommonRes.failed(errorMessage);
        }
        // 检查用户状态是否正常
        if (User.STATUS_LOCK == user.getStatus()) {
            return CommonRes.failed("账号已被锁定,请联系管理员！");
        }

        if (user.getTenantId() != 0) {
            Tenant tenant = tenantService.getById(user.getTenantId());
            if (tenant == null || tenant.getStatus() != CommonConstant.STATUS_ENABLE) {
                return CommonRes.failed("账号所在机构已被禁用,请联系管理员！");
            }
        }

        // 检查是否已经登录，有则踢出
        this.logout(user.getId());
        // 更新用户登录时间
        this.userService.updateLoginTime(user.getId());
        // 保存登录记录
//        LoginLog loginLog = new LoginLog();
//        loginLog.setUserId(user.getName());
//        this.loginLogService.saveLoginLog(loginLog);

//        String token = RecoupUtil.encryptToken(JWTUtil.sign(userId, password));
        String token = JWTUtil.sign(user.getId(), password);
        LocalDateTime expireTime = LocalDateTime.now().plusSeconds(properties.getShiro().getJwtTimeOut());
        String expireTimeStr = DateUtil.formatFullTime(expireTime);
        JWTToken jwtToken = new JWTToken(token, expireTimeStr);
        this.saveTokenToRedis(user, jwtToken);

        // 记录登录时间
        nurseOnlineService.saveLogin(user.getId());
        // 记录事件并推送消息
        eventLogService.procEventLog(null, user.getId(), null, EventEnum.NURSE_WORK_LOGIN.getCode(), null, user.getTenantId());

        Map<String, Object> userInfo = this.generateUserInfo(jwtToken, user);
        return CommonRes.ok(userInfo);
    }

    @Override
    public void logout(Long userId, String token, Integer tenantId) throws RedisConnectException {
        log.info("logout userId: {},token: {}", userId, token);
        if (userId != null && StringUtils.isNotBlank(token)) {
            // 删除 zset中的记录
            Long zremRes = redisService.zrem(CommonConstant.ACTIVE_USERS_ZSET_PREFIX, String.valueOf(userId));
            log.info("logout zem result:{}", zremRes);
            // 删除对应的 token缓存
            Long delRes = redisService.del(CommonConstant.TOKEN_CACHE_PREFIX + token);
            log.info("logout del token result:{}", delRes);
            Long delUserRes = redisService.del(CommonConstant.USER_TOKEN_CACHE_PREFIX + userId);
            log.info("logout del user result:{}", delUserRes);
            // 记录登出时间
            nurseOnlineService.saveLogout(userId);
            // 记录事件并推送消息
            eventLogService.procEventLog(null, userId, null, EventEnum.NURSE_WORK_LOGOUT.getCode(), null, tenantId);
        }
    }

    @Override
    public void logout(Long userId) throws RedisConnectException {
        if (userId != null) {
            ActiveUser activeUser = cacheService.getOnlineUser(userId);
            if (activeUser == null) {
                return;
            }
            String token = activeUser.getToken();
            if (StringUtils.isNotEmpty(token)) {
                log.info("logout userId: {}", userId);
                this.logout(userId, token, activeUser.getTenantId());
            }
        }
    }

    @Override
    public void checkAlive(Long userId) throws RedisConnectException {
        Double zscore = this.redisService.zscore(CommonConstant.ACTIVE_USERS_ZSET_PREFIX, String.valueOf(userId));
        if (zscore == null) {
            // 记录登录时间
            nurseOnlineService.saveLogin(userId);
        }
        // 更新用户在线缓存
        this.updateUserOnlineCache(userId);
    }


    /**
     * 更新用户在线缓存
     * @param userId
     */
    private void updateUserOnlineCache(Long userId) {
        try {
            // 更新zset登录用户score，score 为秒级时间戳
            this.redisService.zadd(CommonConstant.ACTIVE_USERS_ZSET_PREFIX, (double) (System.currentTimeMillis()), String.valueOf(userId));
        } catch (RedisConnectException e) {
            log.error("updateUserOnlineCache error", e);
        }
    }

    /**
     * 生成前端需要的用户信息，包括：
     * 1. token
     * 2. Vue Router
     * 3. 用户角色
     * 4. 用户权限
     * 5. 前端系统个性化配置信息
     *
     * @param token token
     * @param user  用户信息
     * @return UserInfo
     */
    private Map<String, Object> generateUserInfo(JWTToken token, User user) {
        Long userId = user.getId();
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("token", token.getToken());
        userInfo.put("exipreTime", token.getExipreAt());

        Set<String> roles = this.userManager.getUserRoles(userId);
        userInfo.put("roles", roles);

//        Set<String> permissions = this.userManager.getUserPermissions(userId);
//        userInfo.put("permissions", permissions);

//        UserConfig userConfig = this.userManager.getUserConfig(String.valueOf(user.getId()));
//        userInfo.put("config", userConfig);

        user.setPassword("");
        userInfo.put("user", user);

        // 设置主题
        TenantThemeBO themeBO = null;
        if (user.getTenantId() != null && user.getTenantId() != 0) {
            Tenant tenant = tenantService.getById(user.getTenantId());
            if (tenant != null) {
                themeBO = JsonUtils.jsonToBean(tenant.getTheme(), TenantThemeBO.class);
//                themeBO = new TenantThemeBO();
//                themeBO.setBannerColor(tenant.getSiteUrl());
//                themeBO.setBannerUrl(tenant.getTheme());
            }
        }
        // 设置默认主题设置
        if (themeBO == null || themeBO.getBannerColor()==null) {
            themeBO = new TenantThemeBO(defaultBannerUrl, defaultBannerColor);
        }
        userInfo.put("theme", themeBO);
        return userInfo;
    }

    private void saveTokenToRedis(User user, JWTToken token) throws Exception {
        ActiveUser activeUser = new ActiveUser();
        activeUser.setUserId(user.getId());
        activeUser.setRoleId(user.getRoleId());
        activeUser.setTenantId(user.getTenantId());
        activeUser.setToken(token.getToken());
        // zset 存储登录用户，score 为秒级时间戳
        this.redisService.zadd(CommonConstant.ACTIVE_USERS_ZSET_PREFIX, (double) (System.currentTimeMillis()), String.valueOf(user.getId()));
        // redis 中存储用户对应的信息对象， key = 前缀 + userId val=ActiveUser
        this.redisService.set(CommonConstant.USER_TOKEN_CACHE_PREFIX + user.getId(), JsonUtils.objectToJson(activeUser));
        // redis 中存储这个加密 token，key = 前缀 + token
        this.redisService.set(CommonConstant.TOKEN_CACHE_PREFIX + token.getToken(), token.getToken());

    }
    @Resource
    private WxMaService wxMaService;

    @Resource
    private CuserProperties cuserProperties;
    @Override
    public CommonRes appLogin(WxLoginInfo wxLoginInfo) throws Exception {
        log.info("【请求开始】微信登录,请求参数,wxLoginInfo:{}", wxLoginInfo);
        String code = wxLoginInfo.getCode();
        UserInfo userInfo = wxLoginInfo.getUserInfo();
        if (code != null && userInfo != null) {
            Integer shareUserId = wxLoginInfo.getShareUserId();
            String sessionKey = null;
            String openId = null;
            String unionid = null;

            try {
                WxMaJscode2SessionResult result = this.wxMaService.getUserService().getSessionInfo(code);
                sessionKey = result.getSessionKey();
                openId = result.getOpenid();
                unionid = result.getUnionid();
            } catch (WxErrorException var14) {
                var14.printStackTrace();
                return  CommonRes.failed(var14.getError());

            }

            if (sessionKey != null && openId != null) {
                User user = this.userService.findByWxOpenId(openId);
                String ss = AesEncryptUtil.encrypt(openId);
                System.out.println(ss);
                String password = AesEncryptUtil.decrypt(ss);
                if (user == null) {
                    user = new User();
                    user.setName(openId);
                    user.setPassword(password);
                    user.setWeixinOpenid(openId);
                    user.setNickname(userInfo.getNickName());
                    user.setAvatar(userInfo.getAvatarUrl());
                    user.setSsex((int)userInfo.getGender());
                    user.setStatus(User.STATUS_VALID);
                    user.setLastLoginTime(new Date());
                    user.setTenantId(cuserProperties.getTenantId());
                    user.setMobile(System.currentTimeMillis()+"");
                    user.setRoleId(cuserProperties.getRoleId());
                    user.setDeptId(cuserProperties.getDeptId());
                    user.setUnionid(unionid);
                    // 查询是否已关注清雷健康公众号
                    HmpWechatMpFan hmpWechatMpFan = hmpWechatMpFanService.getOne(Wrappers.<HmpWechatMpFan>lambdaQuery().eq(HmpWechatMpFan::getUnionId, unionid));
                    if (hmpWechatMpFan != null) {
                        user.setHealthMpOpenId(hmpWechatMpFan.getOpenId());
                    }
                    this.userService.createUser(user);
                } else {
                    UpdateWrapper wrapper = new UpdateWrapper();
                    wrapper.eq("id",user.getId());
                    wrapper.set("last_login_time",new Date());
                    if(!userService.update(wrapper)){
                        log.error("登录失败");
                        return CommonRes.failed("登录失败");
                    }

                }
                this.logout(user.getId());
                String tockenPassword = MD5Util.encrypt(AesEncryptUtil.decrypt(ss));
                String token = JWTUtil.sign(user.getId(), tockenPassword);
                LocalDateTime expireTime = LocalDateTime.now().plusSeconds(properties.getShiro().getJwtTimeOut());
                String expireTimeStr = DateUtil.formatFullTime(expireTime);
                JWTToken jwtToken = new JWTToken(token, expireTimeStr);
                this.saveTokenToRedis(user, jwtToken);
                // 重新将用户信息加载到 redis中
                cacheService.saveUser(user);
                Map<String, Object> userResult = this.generateUserInfo(jwtToken, user);
                userResult.put("sessionKey",sessionKey);
                //this.redisService.set(CommonConstant.TOKEN_CACHE_SESSIONKEY+user.getId(),sessionKey);

                return CommonRes.ok(userResult);

            } else {
                log.error("微信登录,调用官方接口失败：{}", code);
                return CommonRes.failed();
            }
        } else {
            return CommonRes.failed("获取微信用户信息失败");
        }
    }

    public static void main(String[] args) {
        String ss = AesEncryptUtil.encrypt("123456");
        System.out.println(ss);
        String ss1 = AesEncryptUtil.decrypt(ss);
        String password = MD5Util.encrypt(ss1);
        System.out.println(password);
    }
}
